<?php
	/**
	  * @name		InstallBuilder
	  * @brief		Clase para crear paquetes de instalacion PHP
	  * @author		Hermann D. Schimpf <hschimpf@gschimpf.com>
	  * @license	http://www.gnu.org/licenses/gpl.html
	  *				InstallBuilder - Clase para crear paquetes de instalacion PHP
	  *				Copyright © 2010 Hermann D. Schimpf
	  *				Este programa es software libre: usted puede redistribuirlo y/o modificarlo conforme a los términos de la Licencia Pública General de GNU publicada por la Fundación para el Software Libre, ya sea la versión 3 de esta Licencia o (a su elección) cualquier versión posterior.
	  *				Este programa se distribuye con el deseo de que le resulte útil, pero SIN GARANTÍAS DE NINGÚN TIPO; ni siquiera con las garantías implícitas de COMERCIABILIDAD o APTITUD PARA UN PROPÓSITO DETERMINADO. Para más información, consulte la Licencia Pública General de GNU.
	  *				Junto con este programa, se debería incluir una copia de la Licencia Pública General de GNU. De no ser así, ingrese a http://www.gnu.org/licenses
	  * @date		2010-07-13
	  * @version	0.1.5
  	**/

	final class InstallBuilder extends MyDebuger implements MyVersions, InstallCommands {
		/* version de la clase */
		private static $version			= 0;
		/* subversion de la clase */
		private static $subversion		= 1;
		/* numero de revision de la clase */
		private static $revision		= 5;

		/* titulo del paquete, por defecto 'InstallPackage' */
		private $title				= 'InstallPackage';
		/* descripcion del paquete */
		private $description		= False;
		/* autor del paquete, por defecto el nombre de la clase */
		private $author			= __CLASS__;
		/* directorios base */
		private $baseDirs			= Array();
		/* directorios del paquete */
		private $dirs				= Array();
		/* cantidad de directorios agregados */
		private $dirsCount		= 0;
		/* ficheros del paquete */
		private $files				= Array();
		/* cantidad de ficheros agregados */
		private $filesCount		= 0;
		/* tamano final del paquete (comprimido) */
		private $finalSize		= 0;
		/* fichero para creal el paquete */
		private $packageFile		= False;
		/* objeto conexion */
		private $conexion			= False;
		/* host para la conexion */
		private $dbHost			= False;
		/* usuario para la conexion */
		private $dbUser			= False;
		/* contrasena para la conexion */
		private $dbPass			= False;
		/* nombres de las bases de datos */
		private $dbsName			= Array();
		/* datos de las bases de datos */
		private $dbsData			= Array();
		/* estructura final del paquete */
		private $result			= Array();
		/* clases necesarias localmente */
		private $neededClasses	= Array('Bytes', 'Crypter', 'Installer', 'MySQLProcess');
		/* clases necesarias para el paquete */
		private $buildClasses	= Array('MyException', 'Crypter', 'Installer', 'MySQLProcess');
		/* ruta de las clases para el paquete */
		private $classesPath		= Array();
		/* buffer temporal */
		private $buffer			= '';

		public function __construct() {
			/* llamamos a la creacion del padre */
			parent::__construct(True, __CLASS__);
			/* empezamos a mover el scroll */
			$this->enableAutoScroll();
			/* mostramos un mensaje */
			$this->show('Seteando uso maximo de memoria a ' . $this->bold($this->red('512 MB')) . '..', False);
			/* seteamos el uso maximo de memoria a 512MB */
			ini_set('memory_limit', '512M');
			/* mostramos ok al mensaje */
			$this->showOK();
		}

		public function __destruct() {
			/* matamos el padre */
			parent::__destruct();
		}

		public static final function getVersion() {
			/* retornamos la version actual de la clase */
			return 'v' . self::$version . '.' . self::$subversion . '.' . self::$revision;
		}

		public function setTitle($title = Null) {
			/* llamamos al metodo privado capturando los errores */
			$this->catchExceptions('saveData', 'title', $title, 'Asignando titulo al paquete', 'el titulo para el paquete', False, Null);
		}

		public function setDescription($description = Null) {
			/* llamamos al metodo privado capturando los errores */
			$this->catchExceptions('saveData', 'description', $description, 'Almacenando descripcion del paquete', 'la descripcion para el paquete');
		}

		public function setAuthor($author = Null) {
			/* llamamos al metodo privado capturando los errores */
			$this->catchExceptions('saveData', 'author', $author, 'Almacenando nombre del autor del paquete', 'el nombre del autor');
		}

		public function addDir($dirName = Null) {
			/* llamamos al metodo privado capturando los errores */
			$this->catchExceptions('saveDirs', $dirName);
		}

		public function addFile($fileName = Null) {
			/* llamamos al metodo privado capturando los errores */
			$this->catchExceptions('saveSingleFile', $fileName);
		}

		public function addDB($dbName = Null) {
			/* llamamos al metodo privado capturando los errores */
			$this->catchExceptions('saveDB', $dbName);
		}

		public function setConnectionData($dbHost = Null, $dbUser = Null, $dbPass = Null) {
			/* llamamos al metodo privado capturando los errores */
			$this->catchExceptions('saveConectionData', $dbHost, $dbUser, $dbPass);
		}

		public function buildInstaller() {
			/* llamamos al metodo privado capturando los errores */
			$this->catchExceptions('runBuilder');
		}

		private function verificarClases() {
			/* mostramos un mensaje */
			$this->show('Verificando existencia de clases necesarias..', False);
			/* recorremos las clases necesarias */
			foreach ($this->neededClasses AS $class)
				/* verificamos que exista */
				$this->verificarClase($class);
			/* mostramos ok al mensaje */
			$this->showOK();
		}

		private function saveDirs($dirName) {
			/* eliminamos la barra final del directorio */
			$dirName = preg_replace('/(\/)$/', '', $dirName);
			/* mostramos un mensaje */
			$this->show('Agregando el directorio ' . $this->fileFormat($dirName) . ' al paquete..', False);
			/* realizamos algunas verificaciones */
			$this->verificarDir($dirName);
			/* almacenamos el directorio base */
			$this->baseDirs[] = $dirName;
			/* mostramos ok al mensaje */
			$this->showOK();
			/* agregamos el contenido del directorio */
			$this->addDirRecursive($dirName);
			/* mostramos la cantidad de ficheros y directorios agregados */
			$this->showCounters();
		}

		private function saveSingleFile($fileName) {
			/* mostramos un mensaje */
			$this->show('Agregando el fichero ' . $this->fileFormat($fileName, True) . ' al paquete..', False);
			/* verificamos el fichero */
			$this->verificarFile($fileName);
			/* agregamos el fichero al array de ficheros */
			$this->files[]['fileName'] = substr(strrchr($fileName, '/'), 1);
			/* agregamos el nombre del directorio base */
			$this->files[count($this->files) - 1]['baseDir'] = substr(preg_replace('/(' . substr(strrchr($fileName, '/'), 1) . ')$/', '', $this->getFullDir($fileName)), 0, -1);
			/* aumentamos la cantidad de ficheros agregados */
			$this->filesCount++;
			/* mostramos ok al mensaje */
			$this->showOK();
		}

		private function verificarDir($dirName) {
			/* verificamos si especifico un directorio */
			if ($dirName === Null)
				/* salimos con una excepcion */
				$this->newException('No se especifico el directorio a agregar');
			/* verificamos si el directorio existe */
			if (!realpath($dirName))
				/* salimos con una excepcion */
				$this->newException('El directorio ' . $this->fileFormat($dirName) . ' no existe');
			/* verificamos si es un directorio */
			if (!is_dir($this->getFullDir($dirName)))
				/* salimos con una excepcion */
				$this->newException('La ruta ' . $this->fileFormat($dirName) . ' no es un directorio');
		}

		private function verificarFile($fileName) {
			/* verificamos si especifico un fichero */
			if ($fileName === Null)
				/* salimos con una excepcion */
				$this->newException('No se especifico el fichero a agregar');
			/* verificamos si el fichero existe */
			if (!file_exists($fileName))
				/* salimos con una excepcion */
				$this->newException('El fichero ' . $this->fileFormat($fileName) . ' no existe');
			/* verificamos si es un fichero */
			if (!is_file($fileName))
				/* salimos con una excepcion */
				$this->newException('La ruta ' . $this->fileFormat($fileName) . ' no es un fichero');
		}

		private function addDirRecursive($dirName) {
			/* recorremos los archivos del directorio */
			foreach (glob($this->getFullDir($dirName) . '/*') AS $fileName) {
				/* verificamos si el fichero actual es un directorio */
				if (!$this->isDir($fileName))
					/* agregamos el fichero */
					$this->saveFile($this->getBaseDir($fileName));
			}
		}

		private function isDir($dirName) {
			/* obtenemos el directorio de trabajo actual */
			$actualDir = getcwd();
			/* nos posicionamos en la raiz */
			chdir('/');
			/* verificamos si el fichero es un directorio */
			if (!is_dir($this->getFullDir($dirName))) {
				/* nos posicionamos en el directorio de trabajo original */
				chdir($actualDir);
				/* retornamos false */
				return False;
			}
			/* nos posicionamos en el directorio de trabajo original */
			chdir($actualDir);
			/* agregamos el directorio */
			$this->saveDir($this->getBaseDir($dirName));
			/* agregamos el directorio */
			$this->addDirRecursive($dirName);
			/* retornamos True */
			return True;
		}

		private function saveConectionData($dbHost, $dbUser, $dbPass) {
			/* almacenamos el usuario de la DB */
			$this->saveData('dbHost', ($dbHost === Null ? 'localhost' : $dbHost), 'Almacenando host para la conexion', 'el host para la conexion');
			/* almacenamos el usuario de la DB */
			$this->saveData('dbUser', $dbUser, 'Almacenando usuario para la conexion', 'el usuario para la conexion');
			/* almacenamos la contrasena de la DB */
			$this->saveData('dbPass', $dbPass, 'Almacenando contrasena para la conexion', 'la contrasena para la conexion');
		}

		private function saveDB($dbName) {
			/* almacenamos el nombre de la DB */
			$this->saveData('dbsName', $dbName, 'Agregando la dase de datos ' . $this->italic($this->blue($dbName)), 'el nombre de la base de datos', True);
		}

		private function runBuilder() {
			/* verificamos que existan las clases necesarias */
			$this->verificarExistenciaClases();
			/* compilamos los ficheros */
			$this->buildFiles();
			/* compilamos la base de datos */
			$this->buildDB();
			/* armamos el paquete */
			$this->buildPackage();
		}

		private function verificarExistenciaClases() {
			/* mostramos un mensaje */
			$this->show('Iniciando creacion del paquete de instalacion ' . $this->italic($this->blue($this->getTitle())) . '..', False);
			/* recorremos las clases necesarias */
			foreach ($this->neededClasses AS $class)
				/* verificamos que exista */
				$this->verificarClase($class);
			/* mostramos ok al mensaje */
			$this->showOK();
		}

		private function buildFiles() {
			/* almacenamos la estructura de directorios */
			$this->buildDirStructure();
			/* almacenamos los archivos */
			$this->buildFilesPackage();
			/* comprimimos los ficheros */
			$this->compressFiles();
		}

		private function buildDirStructure() {
			/* mostramos un mensaje */
			$this->show('Comprimiendo estructura de directorios..', False);
			/* ciframos la cadena */
			$this->dirs = Crypter::encrypt($this->dirs, $this->getKey());
			/* mostramos ok al mensaje */
			$this->showOK();
		}

		private function buildFilesPackage() {
			/* mostramos un mensaje */
			$this->show('Procesando contenido de los ficheros..');
			/* recorremos los ficheros */
			foreach ($this->files AS $file)
				/* procesamos el fichero */
				$this->processFile($file);
		}

		private function processFile($file) {
			/* mostramos un mensaje */
			$this->show('Comprimiendo contenido del fichero: ' . $this->fileFormat($file['fileName']), False);
			/* obtenemos el tamano del fichero */
			$this->getSize($file);
			/* obtenemos el contenido del fichero */
			$this->getContents($file);
			/* mostramos ok al mensaje */
			$this->showOK();
		}

		private function getSize($file) {
			/* obtenemos el tamano del fichero y lo convertimos el tamano a un valor en KB, MB, etc.. */
			$size = Bytes::convert(filesize($file['baseDir'] . '/' . $file['fileName']));
			/* almacenamos el tamano */
			$this->result[$file['fileName']]['size'] = $size;
			/* mostramos el tamano */
			$this->show($this->blue(' (' . $size . ')') . '..', False);
		}

		private function getContents($file) {
			/* obtenemos el contenido del fichero */
			$contents = file_get_contents($file['baseDir'] . '/' . $file['fileName']);
			/* ciframos el contenido del fichero y lo almacenamos */
			$this->result[$file['fileName']]['contents'] = Crypter::encrypt($contents, preg_replace('/(\.\.\/|\.\/)+/', '', $file['fileName']));
		}

		private function compressFiles() {
			/* mostramos un mensaje */
			$this->show('Comprimiendo paquete de ficheros..', False);
			/* almacenamos los ficheros */
			$this->files = Crypter::encrypt($this->result, $this->getKey());
			/* reseteamos el array */
			$this->result = Array();
			/* mostramos ok al mensaje */
			$this->showOK();
		}

		private function buildDB() {
			/* mostramos un mensaje */
			$this->show('Iniciando descarga de la base de datos..', False);
			/* verificamos si se especifico una db */
			if (count($this->dbsName) === 0) {
				/* mostramos un mensaje */
				$this->show($this->red('No se especifico ninguna base de datos'));
				/* salimos del metodo */
				return False;
			} else
				/* mostramos ok al mensaje */
				$this->showOK();
			/* realizamos la conexion */
			$this->conectarDB();
			/* recorremos las bases de datos */
			foreach ($this->dbsName AS $dbName) {
				/* mostramos un mensaje */
				$this->show('Procesando la base de datos ' . $this->italic($this->blue($dbName)) . '..', False);
				/* seleccionamos la DB */
				$this->conexion->seleccionarDB($dbName);
				/* mostramos ok al mensaje */
				$this->showOK();
				/* obtenemos la lista de tablas */
				$this->getTablesList($dbName);
				/* obtenemos el SQL de creacion de cada tabla */
				$this->getCreateSql($dbName);
			}
			/* eliminamos el objeto conexion ya que no lo volveremos a utilizar */
			unset($this->conexion);
		}

		private function conectarDB() {
			/* mostramos un mensaje */
			$this->show('Conectando al servidor de Bases de Datos..', False);
			/* verificmaos si se especificaron las configuraciones */
			if ($this->dbHost === False)
				/* salimos con una excepcion */
				$this->newException('No se especificaron los datos de conexion al servidor');
			/* creamos la instancia de MySQLProcess */
			$this->conexion = new MySQLProcess;
			/* asignamos el host */
			$this->conexion->setHost($this->dbHost);
			/* asignamos el usuario */
			$this->conexion->setUser($this->dbUser);
			/* asignamos la contrasena */
			$this->conexion->setPass($this->dbPass);
			/* verificamos si logramos conectar */
			if ($this->conexion->conectar() !== True)
				/* salimos con una excepcion */
				$this->newException('No se pudo conectar con el servidor');
			/* mostramos ok al mensaje */
			$this->showOK();
		}

		private function getTablesList($dbName) {
			/* mostramos un mensaje */
			$this->show('Obteniendo lista de tablas..', False);
			/* ejecutamos el SQL */
			$this->conexion->ejecutarSQL('SHOW TABLES');
			/* recorremos los resultados */
			while ($table = $this->conexion->getResultadoSQL())
				/* agregamos la tabla a la lista */
				$this->dbsData[$dbName]['list'][] = $table->{'Tables_in_' . $dbName};
			/* mostramos ok al mensaje */
			$this->showOK();
			/* mostramos la cantidad de tablas */
			$this->show('Cantidad de tablas: ' . $this->bold(count($this->dbsData[$dbName]['list'])));
		}

		private function getCreateSql($dbName) {
			/* mostramos un mensaje */
			$this->show('Obteniendo contenido de las tablas..');
			/* recorremos las tablas */
			foreach ($this->dbsData[$dbName]['list'] AS $table)
				/* procesamos la tabla */
				$this->processTable($dbName, $table);
			/* eliminamos la lista de tablas de la base de datos */
			unset($this->dbsData[$dbName]['list']);
		}

		private function processTable($dbName, $table) {
			/* mostramos un mensaje */
			$this->show('Procesando tabla: ' . $this->fileFormat($table) . '..', False);
			/* obtenemos la estructura de la tabla */
			$this->getTableStructure($dbName, $table);
			/* obtenemos los datos de la tabla */
			$this->getTableData($dbName, $table);
			/* mostramos ok al mensaje */
			$this->showOK();
		}

		private function getTableStructure($dbName, $table) {
			/* ejecutamos un SQL para obtener la estructura de la tabla */
			$this->conexion->ejecutarSQL('SHOW CREATE TABLE ' . $table);
			/* obtenemos el resultado de la consulta */
			$cadena = $this->conexion->getResultadoSQL();
			/* obtenemos la cadena de creacion */
			$this->dbsData[$dbName][$table]['structure'] = Crypter::encrypt($cadena->{'Create Table'}, $table);
		}

		private function getTableData($dbName, $table) {
			/* obtenemos los campos de la tabla */
			$tableRows = $this->getTableRows($table);
			/* creamos una variable temporal */
			$rowsCount = 0;
			/* creamos el espacio para los datos */
			$this->dbsData[$dbName][$table]['data'] = Array();
			/* ejecutamos un SQL para obtener los datos de la tabla */
			$this->conexion->ejecutarSQL('SELECT * FROM ' . $table);
			/* obtenemos los resultados */
			while ($result = $this->conexion->getResultadoSQL()) {
				/* sumamos una fila */
				$rowsCount++;
				/* creamos un sql temporal */
				$sqlString = 'INSERT INTO ' . $table . ' VALUES (';
				/* recorremos los campos de la tabla */
				foreach ($tableRows AS $tableRow) {
					/* obtenemos el valor */
					$value = $result->$tableRow['name'];
					/* verificamos si el valor no es nulo */
					if ($value !== Null) {
						/* verificamos el tipo de dato */
						switch ($tableRow['type']) {
							/* si es algun numero */
							case 'tinyint': case 'smallint': case 'mediumint': case 'int': case 'bigint': case 'decimal': case 'float': case 'double': case 'real': case 'bit': case 'bool': case 'serial':
								/* agregamos el valor sin comillas */
								$sqlString .= $value;
								/* saltamos */
								break;
							/* si es alguna fecha o string, etc.. */
							default:
								/* agregamos el valor entre comillas */
								$sqlString .= '\'' . addslashes($value) . '\'';
								/* saltamos */
								break;
						}
					} else
						/* agregamos el valor null */
						$sqlString .= 'Null';
					/* agregamos una coma de separacion al sql */
					$sqlString .=  ',';
				}
				/* eliminamos la coma final del sql, cerramos el SQL y agregamos el Insert a los datos de la tabla */
				$this->dbsData[$dbName][$table]['data'][] = substr($sqlString, 0, -1) . ')';
			}
			/* ciframos el contenido de la tabla */
			$this->dbsData[$dbName][$table]['data'] = Crypter::encrypt($this->dbsData[$dbName][$table]['data'], $table);
			/* mostramos la cantidad de filas obtenidas */
			$this->show('Cantidad de registros obtenidos: ' . $this->bold($rowsCount) . '..', False);
		}

		private function getTableRows($table) {
			/* creamos un array temporal */
			$tableRows = Array();
			/* ejecutamos un SQL para obtener la estructura de la tabla */
			$this->conexion->ejecutarSQL('DESC ' . $table);
			/* obtenemos los resultados */
			while ($tableRow = $this->conexion->getResultadoSQL()) {
				/* almacenamos el nombre del campo */
				$tableRows[]['name'] = $tableRow->Field;
				/* almacenamos el tipo del campo */
				$tableRows[count($tableRows) - 1]['type'] = strtolower(preg_replace('/\(.+/', '', $tableRow->Type));
			}
			/* retornamos el nombre de los campos */
			return $tableRows;
		}

		private function buildPackage() {
			/* combinamos los datos */
			$this->combineData();
			/* abrimos el fichero */
			$this->openDestFile();
			/* iniciamos el codigo PHP en el paquete */
			$this->write('<?php');
			/* agregamos el contenido del fichero */
			$this->writePackageContents();
			/* finalizamos el codigo PHP del paquete */
			$this->write('?>');
			/* cerramos el fichero y mostramos el tamano final */
			$this->closeFile();
		}

		private function combineData() {
			/* agregamos la estructura de directorios al paquete */
			$this->result['tree'] = $this->dirs;
			/* agregamos los ficheros al paquete */
			$this->result['files'] = $this->files;
			/* agregamos la base de datos al paquete final */
			$this->result['db'] = (
				/* verificamos si se especifico una DB */
				count($this->dbsName) === 0 ?
				/* seteamos false */
				False :
				/* agregamos el contenido de la db */
				Crypter::encrypt($this->dbsData, $this->getKey())
			);
		}

		private function openDestFile() {
			/* mostramos un mensaje */
			$this->show('Iniciando creacion del paquete de instalacion..', False);
			/* creamos el nombre del fichero */
			$fileName = preg_replace('/\s+/', '_', $this->getTitle()) . '.php';
			/* verificamos si ya existe un fichero con el mismo nombre */
			if (file_exists($fileName))
				/* mostramos una aclaracion */
				$this->show($this->red('Se sobreescribe el fichero existente..'), False);
			/* abrimos el fichero */
			$this->packageFile = fopen($fileName, 'w');
			/* mostramos ok al mensaje */
			$this->showOK();
		}

		private function writePackageContents() {
			/* agregamos los creditos */
			$this->addCredits();
			/* agregamos las clases necesarias */
			$this->addNeededClasses();
			/* agregamos la instancia del instalador */
			$this->instantiateInstaller();
		}

		private function addCredits() {
			/* iniciamos los creditos */
			$this->write("/*");
			/* agregamos el created by */
			$this->write('Created by:     ' . __CLASS__ . ' ' . InstallBuilder::getVersion(), 1);
			/* agregamos el contact */
			$this->write('Contact To:     hschimpf@gschimpf.com', 1);
			/* agregamos una linea vacia */
			$this->write('');
			/* agregamos el nombre del paquete */
			$this->write('Package Name:   ' . $this->getTitle(), 1);
			/* verificamos si el paquete tiene una descripcion */
			if ($this->getDescrip() !== False)
				/* agregamos la descripcion del paquete */
				$this->write('Description:    ' . $this->getDescrip(), 1);
			/* agregamos el nombre del autor del paquete */
			$this->write('Author:         ' . $this->getAuthor(), 1);
			/* agregamos la fecha de creacion */
			$this->write('Build Date:     ' . date('Y-m-d H:i:s'), 1);
			/* agregamos una linea vacia */
			$this->write('');
			/* agregamos una linea para el recuadro */
			$this->write('************************************', 1);
			/* agregamos una aclaracion */
			$this->write('** NOT MODIFY THIS FILE MANUALLY! **', 1);
			/* agregamos una linea para el recuadro */
			$this->write('************************************', 1);
			/* agregamos un mensaje para el usuario */
			$this->write('Please set the maximum execution time in 300 seconds or more.', 1);
			/* finalizamos los creditos */
			$this->write("*/");
		}

		private function addNeededClasses() {
			/* mostramos un mensaje */
			$this->show('Agregando clases de instalacion al paquete..');
			/* agregamos un mensaje en el ficher */
			$this->write('/* clases necesarias para la instalacion */');
			/* recorremos las clases */
			foreach ($this->buildClasses AS $class)
				/* agregamos la clase */
				$this->addClass($class);
			/* recorremos las clases */
			foreach ($this->classesPath AS $classFile => $className)
				/* agregamos la clase al fichero */
				$this->addClassToFile($classFile, $className);
			/* almacenamos el buffer en el fichero */
			$this->write('eval(gzuncompress(base64_decode(\'' . base64_encode(gzcompress($this->buffer, 9)) . '\')));');
		}

		private function addClass($class, $verificar = True) {
			/* verificamos si verificamos */
			if ($verificar)
				/* verificamos si la clase existe */
				$this->verificarClase($class);
			/* creamos una instancia para obtener los datos de la clase */
			$datos = new ReflectionClass($class);
			/* obtenemos el padre */
			$parent = $datos->getParentClass();
			/* obtenemos las interfaces */
			$interfaces = $datos->getInterfaces();
			/* recorremos las interfaces */
			foreach ($interfaces AS $interface)
				/* agregamos primero la interfaz */
				$this->addClass($interface->name, False);
			/* verificamos si tiene padre */
			if ($parent)
				/* agregamos primero el padre */
				$this->addClass($parent->name);
			/* verificamos si hay un fichero (False cuando la clase es de PHP) */
			if ($datos->getFileName())
				/* agregamos la ruta del fichero de la clase */
				$this->classesPath[$datos->getFileName()] = $class;
		}

		private function addClassToFile($classPath, $className) {
			/* mostramos un mensaje */
			$this->show('Agregando la clase ' . $this->classFormat($className) . ' al paquete de instalacion..', False);
			/* obtenemos el contenido del fichero */
			$classFile = file_get_contents($classPath);
			/* eliminamos los comentarios y el inicio php '<?php' */
			$classFile = preg_replace(Array('/(\/{2}.*|\/\*[\S|\s]*?\*\/)/', '/^((\s+)?<\?php(\s+)?)/'), Null, $classFile);
			/* purgamos los espacios */
			$classFile = preg_replace('/\s+/', ' ', $classFile);
			/* verificamos si la clase esta en el fichero */
			if (!preg_match('/(\s)?(class|interface)\s+'.$className.'/', $classFile))
				/* salimos con una excepcion */
				$this->newException('La clase o interface no se encuentra en el fichero ' . $this->fileFormat($classPath, True));
			/* eliminamos el final php '?>' */
			$classFile = preg_replace('/\s*?\?>$/', Null, $classFile);
			/* agregamos la clase al fichero */
			$this->buffer .= 'eval(gzuncompress(base64_decode(\'' . base64_encode(gzcompress($classFile, 9)) . '\')));';
			/* mostramos ok al mensaje */
			$this->showOK();
		}

		private function instantiateInstaller() {
			/* mostramos un mensaje */
			$this->show('Agregando datos de instalacion al paquete..', False);
			/* creamos el nombre del objeto */
			$objectName = 'InstallerInstance';
			/* agregamos una linea de aclaracion al paquete */
			$this->write('/* instanciamos la clase Installer */');
			/* agregamos la instancia de Installer */
			$this->write('$' . $objectName . ' = new Installer(\'' . base64_encode($this->getTitle()) . '\', \'' . base64_encode($this->getAuthor()) . '\', ' . (
				/* verificamos si tiene descripcion */
				$this->getDescrip() !== False ?
				/* enviamos la descripcion */
				'\'' . base64_encode($this->getDescrip()) . '\'' :
				/* enviamos null */
				'Null'
			) . ', ' . (
				/* verificamos si hay bases de datos */
				count($this->dbsName) === 0 ?
				/* enviamos false */
				'False' :
				/* enviamos True */
				'True'
			/* finalizamos la cadena */
			) . ');');
			/* agregamos una linea de aclaracion al paquete */
			$this->write('/* verificamos si los valores de instalacion se especificaron */');
			/* agregamos una condicion */
			$this->write('if (!$' . $objectName . '->configLoaded())');
			/* agregamos una linea de aclaracion al paquete */
			$this->write('/* solicitamos los valores de instalacion */', 1);
			/* agregamos la llamada al metodo getInstallConfig */
			$this->write('$' . $objectName . '->getInstallConfig();', 1);
			/* agregamos el else de la condicion */
			$this->write('else {');
			/* agregamos una linea de aclaracion al paquete */
			$this->write('/* cargamos los datos de la instalacion */', 1);
			/* agregamos la carga de datos del paquete */
			$this->write('$' . $objectName . '->loadData(\'' . preg_replace('/\s+/', '_', $this->getTitle()) . '.php\');', 1);
			/* agregamos una linea de aclaracion al paquete */
			$this->write('/* ejecutamos la instalacion */', 1);
			/* agregamos la carga de datos del paquete */
			$this->write('$' . $objectName . '->installPackage();', 1);
			/* finalizamos el else de la condicion */
			$this->write('}');
			/* escribimos los datos de la instalacion */
			$this->write('/**' . base64_encode(serialize($this->result)) . '**/');
			/* mostramos ok al mensaje */
			$this->showOK();
		}

		private function closeFile() {
			/* mostramos un mensaje */
			$this->show('Finalizando paquete de instalacion..', False);
			/* cerramos el fichero */
			fclose($this->packageFile);
			/* mostramos ok al mensaje */
			$this->showOK();
			/* mostramos el tamano final del paquete */
			$this->show('Tamano final del paquete: ' . $this->blue(Bytes::convert(filesize(preg_replace('/\s+/', '_', $this->getTitle()) . '.php'))));
		}

		private function write($str, $tabs = 0, $break = True) {
			/* iniciamos un bucle */
			for ($i = 0; $i < $tabs; $i++)
				/* agregamos un tabulador */
				fwrite($this->packageFile, "\t");
			/* grabamos el string en el fichero */
			fwrite($this->packageFile, $str . ($break ? "\n" : ''));
		}

		private function getTitle() {
			/* retornamos el titulo */
			return $this->title;
		}

		private function getAuthor() {
			/* retornamos el nombre del autor */
			return $this->author;
		}

		private function getDescrip() {
			/* retornamos la descripcion del paquete */
			return $this->description;
		}

		private function getKey() {
			/* retornamos una cadena para la clave */
			return md5(preg_replace('/\s+/', '_', $this->getTitle()) . $this->getAuthor());
		}

		private function saveData($attr, $value, $msg, $errorMsg, $array = False) {
			/* mostramos un mensaje */
			$this->show($msg . '..', False);
			/* verificamos si especifico un valor */
			if ($value === Null)
				/* salimos con una excepcion */
				$this->newException('No se especifico ' . $errorMsg);
			/* verificamos si el atributo existe */
			if (!isset($this->$attr))
				/* salimos con una excepcion */
				$this->newException('El atributo ' . $this->italic($attr) . ' no existe');
			/* verificamo si agregamos como array */
			if ($array === True)
				/* agregamos el valor al array */
				$this->{$attr}[] = $value;
			else
				/* almacenamos el valor */
				$this->$attr = $value;
			/* mostramos ok al mensaje */
			$this->showOK();
		}

		private function saveDir($dirName, $show = True) {
			/* verificamos si mostramos el mensaje */
			if ($show)
				/* mostramos un mensaje */
				$this->show('Agregando el directorio ' . $this->fileFormat($this->getBaseDir($dirName)) . ' al paquete..', False);
			/* agregamos el directorio al array de directorios */
			$this->dirs[] = preg_replace('/(\.\.\/|\.\/)+/', '', $dirName);
			/* aumentamos la cantidad de directorios agregados */
			$this->dirsCount++;
			/* verificamos si mostramos el mensaje */
			if ($show)
				/* mostramos ok al mensaje */
				$this->showOK();
		}

		private function saveFile($fileName) {
			/* mostramos un mensaje */
			$this->show('Agregando el fichero ' . $this->fileFormat($this->getBaseDir($this->baseDirs[count($this->baseDirs) - 1] . '/' . $fileName)) . ' al paquete..', False);
			/* agregamos el fichero al array de ficheros */
			$this->files[]['fileName'] = $fileName;
			/* agregamos el nombre del directorio base */
			$this->files[count($this->files) - 1]['baseDir'] = $this->baseDirs[count($this->baseDirs) - 1];
			/* aumentamos la cantidad de ficheros agregados */
			$this->filesCount++;
			/* mostramos ok al mensaje */
			$this->showOK();
		}

		private function showCounters() {
			/* mostramos la cantidad de directorios agregados */
			$this->show('Subdirectorios agregados: ' . $this->bold($this->dirsCount));
			/* vaciamos el contador de directorios */
			$this->dirsCount = 0;
			/* mostramos la cantidad de ficheros agregados */
			$this->show('Ficheros agregados: ' . $this->bold($this->filesCount));
			/* vaciamos el contador de ficheros */
			$this->filesCount = 0;
		}

		private function getFullDir($file) {
			/* agregamos la ruta absoluta del fichero */
			$file = (
				/* verificamos si inicia con / */
				preg_match('/^\//', $file) ?
				/* enviamos la ruta como esta */
				$file : (
					/* verificamos si ya tiene la ruta absoluta */
					strstr($file, getcwd()) ?
					/* enviamos la ruta como esta */
					$file :
					/* agregamos la ruta absoluta */
					getcwd() . '/' . $file
				)
			);
			/* eliminamos las barras repetidas */
			$file = preg_replace('/\/\/+/', '/', $file);
			/* retornamos la cadena */
			return $file;
		}

		private function getBaseDir($file) {
			/* eliminamos la ruta en la que estamos actualmente posicionados de la ruta al fichero fichero */
			return preg_replace('/^(' . str_replace('/', '\/', $this->baseDirs[count($this->baseDirs) - 1]) . '\/)/', '', $file);
		}

		private function catchExceptions($method = Null) {
			/* ingresamos a un control de errores para capturar mis excepciones */
			try {
				/* ingresamos a un control de errores para capturar las excepciones de ejecucion */
				try {
					/* realizamos algunas verificaciones */
					$this->verificarMetodo($method);
					/* obtenemos los parametros recibidos */
					$args = func_get_args();
					/* llamamos al metodo con los parametros */
					call_user_func_array(Array(__CLASS__, $method), $this->buildArgs($args));
				/* capturamos las excepciones */
				} catch (Exception $e) {
					/* capturamos todos los errores y los transformamos en mi excepcion */
					$this->newException($e);
				}
			/* capturamos mis excepciones */
			} catch (MyException $e) {
				/* si se produjo una excepcion la manejamos con el metodo 'show' */
				$this->show($e->getMensaje());
			}
		}
	}
?>